Projekt Dokumentation

Aufgabe: Vorhersage der Umsätze vom 9.6.2019 bis 30.07.2019

Infos zu den gegebenen Daten

Warengruppen: * 1 = Brot * 2 = Brötchen * 3 = Croissant * 4 = Konditorei * 5 = Kuchen * 6 = Saisonbrot

Saisonbrot muss nicht vorhergesagt werden! Siehe ‘predition_template.csv’.

Wetterdaten: * Mittlerer Bewölkungsgrad am Tag (0 = min, 8 = max) * MIttlere Temperatur in C * Mittlere Windgeschwindigkeit in m/s * Wettercode (http://www.seewetter-kiel.de/seewetter/daten_symbole.htm) * und in der Datei wettercodes.Rda

Vorbereitung & benötigte Libraries laden

remove(list = ls())
# Create list with needed libraries
# Quellen:
#   1. synthpop: https://cran.r-project.org/web/packages/synthpop/vignettes/synthpop.pdf
#   2. 
pkgs <- c("lubridate", "stringr","tidyverse", "readr", 
          "fastDummies", "reticulate", "ggplot2", "Metrics", "VIM", "synthpop", "httr")

# Load each listed library and check if it is installed and install if necessary
for (pkg in pkgs) {
  if (!require(pkg, character.only = TRUE)) {
    install.packages(pkg)
    library(pkg, character.only = TRUE)
  }
}

Vorbereitete Datensätze laden

Skripte, in denen die Daten aufbereitet wurden: - Wetterdaten –> “Datenaufbereitung_Wetter.Rmd” - Feiertagedaten –> “Datenaufbereitung_Feiertage.R” - Schulferien –> “Datenaufbereitung_Schulferien.R” - Umsatzdaten –> “Datenaufbereitung_Umsatz.R” - Umsatzdaten aus dem Facheinzelhandel –> “??”

# Lade Daten
load("pj_wetter_dummy.Rda")
pj_wetter <- pj_wetter_dummy
  
load("kiwoDT.Rda")
pj_kiwo <- kiwoDT
  
load("pj_umsatz.Rda")

load("schulferien.Rda")
pj_schulferien <- schulferien

load("umsatzFachEinzelHandelSH.Rda")

# Erste Betrachtung der Daten
#summary(pj_wetter)
#summary(pj_kiwo)
#summary(pj_umsatz)

Erstellung des Trainingsdatensatzen

# Merge erstellt automatisch die Schnittmenge
# Der Zusatz all.x = TRUE sorgt dafür, dass keine Zeilen (basierend auf Datensatz x) weggelöscht werden
# Wetterdaten nach Datum hinzufügen
pj_umsatz_wetter <- merge(pj_umsatz, pj_wetter, by="Datum", all.x = TRUE)

# Schulferien nach Datum hinzufügen
pj_umsatz_wetter_ferien <- merge(pj_umsatz_wetter, pj_schulferien, by="Datum", all.x = TRUE)

# KiWo nach Datum hinzufügen
allData <- merge(pj_umsatz_wetter_ferien, pj_kiwo, by="Datum", all.x = TRUE)

allData <- merge(allData, umsatzFachEinzelHandelSH, by="Datum", all.x = TRUE)

# auf fehlende Werte überprüfen:
allData_na <- allData %>%
  aggr(combined=TRUE, numbers=TRUE)
Warning: not enough horizontal space to display frequencies

# Imputation Temperatur und Windstaerke
# Aktuell: "Datenspende" vom Wert vom Vortag
# ZIEL: Mittelwert aus Temperatur von Vortag und Tag danach -> Armando! :)
allData <- allData %>%  
  hotdeck(variable = c("Temperatur", "Windstaerke"),
          ord_var = "Datum")

#imputierte Werte graphisch überprüfen:
ggplot(allData) +
  geom_point(aes(x = Datum, y = Temperatur, color = Temperatur_imp))

ggplot(allData) +
  geom_point(aes(x = Datum, y = Windstaerke, color = Windstaerke_imp))


# NA Wettercodes zu 0, da Spalte WC_NA angibt, wo Wettercodes gefehlt haben
# Spalten 12 -24

# das gleiche gilt bei der Bewölkung
# Spalten 26 - 29

# weitere NA mit 0 füllen, dort wo es Sinn ergibt  

allData <- allData %>%
    mutate_at(c(12:34), ~replace(., is.na(.), 0))

# generating synthetic data 
synthpop_allData <- syn(allData)[["syn"]]

Variable(s): Wochentag have been changed for synthesis from character to factor.
Warning: In your synthesis there are numeric variables with 5 or fewer levels: WC_Bewölkung_nicht_beobachtet, WC_Bewölkung_zunehmend, WC_Dunst_Staub, WC_Ereignisse_letzte_h, WC_Gewitter, WC_Nebel_Eisnebel, WC_Regen, WC_Schnee, WC_Sprühregen, WC_Trockenereignisse, WC_NA, Bewoelkungsgrad_gering, Bewoelkungsgrad_keine, Bewoelkungsgrad_mittel, Bewoelkungsgrad_stark, Bewoelkungsgrad_NA, Schulferien, KielerWoche, Temperatur_imp.
Consider changing them to factors. You can do it using parameter 'minnumlevels'.

Variable(s): WC_Bewölkung_abnehmend, WC_Bewölkung_gleichbleibend, WC_Schauer numeric but with only 1 or fewer distinct values turned into factor(s) for synthesis.

Variable WC_Bewölkung_abnehmend has only one value so its method has been changed to "constant".
Variable WC_Bewölkung_abnehmend removed as predictor because only one value.
Variable WC_Bewölkung_gleichbleibend has only one value so its method has been changed to "constant".
Variable WC_Bewölkung_gleichbleibend removed as predictor because only one value.
Variable WC_Schauer has only one value so its method has been changed to "constant".
Variable WC_Schauer removed as predictor because only one value.
Variables Temperatur_imp, Windstaerke_imp are collinear. Variables later in 'visit.sequence'
are derived from Temperatur_imp.


Synthesis
-----------
 Datum Brot Brötchen Croissant Konditorei Kuchen Saisonbrot Wochentag Konditorei_imp Windstaerke
 Temperatur WC_Bewölkung_abnehmend WC_Bewölkung_gleichbleibend WC_Bewölkung_nicht_beobachtet WC_Bewölkung_zunehmend WC_Dunst_Staub WC_Ereignisse_letzte_h WC_Gewitter WC_Nebel_Eisnebel WC_Regen
 WC_Schauer WC_Schnee WC_Sprühregen WC_Trockenereignisse WC_NA Bewoelkungsgrad_gering Bewoelkungsgrad_keine Bewoelkungsgrad_mittel Bewoelkungsgrad_stark Bewoelkungsgrad_NA
 Schulferien KielerWoche UmsatzFEH Temperatur_imp Windstaerke_imp
# dummy coding der Wochentage
allData_dummy <- dummy_cols(allData, select_columns = "Wochentag")
synthpop_allData_dummy <- dummy_cols(synthpop_allData, select_columns = "Wochentag")

allData_dummy$year <- year(allData_dummy$Datum)
allData_dummy$month <- month(allData_dummy$Datum)
allData_dummy$day <- day(allData_dummy$Datum)
synthpop_allData_dummy$year <- year(synthpop_allData_dummy$Datum)
synthpop_allData_dummy$month <- month(synthpop_allData_dummy$Datum)
synthpop_allData_dummy$day <- day(synthpop_allData_dummy$Datum)

save(allData_dummy, file="projectData_dummy_D.Rda")
save(synthpop_allData_dummy, file = "projectSynthpopData_dummy_D.Rda")

allData_dummy$Datum <- NULL
synthpop_allData_dummy$Datum <- NULL

#summary(allData_dummy)
save(allData_dummy, file="projectData_dummy.Rda")
save(synthpop_allData_dummy, file = "projectSynthpopData_dummy.Rda")

Testdatensatz

# Erstelle einen leeren Dataframe mit einer Spalte für das Datum
testDatenSatz <- data.frame(Datum = character())

# Erstelle eine Sequenz von Daten im angegebenen Zeitraum
datum_sequenz <- seq(from = as.Date("2019-06-09"),
                     to = as.Date("2019-07-30"),
                     by = "days")

# Füge die Daten der Sequenz dem Dataframe hinzu
sBrot <- select(pj_umsatz, "Datum", "Saisonbrot")
testDatenSatz <- rbind(testDatenSatz, data.frame(Datum = datum_sequenz))
testDatenSatz$Wochentag <- weekdays(testDatenSatz$Datum)
testDatenSatz <- merge(testDatenSatz, pj_wetter, by="Datum", all.x = TRUE)
testDatenSatz <- merge(testDatenSatz, pj_schulferien, by="Datum", all.x = TRUE)
testDatenSatz <- merge(testDatenSatz, pj_kiwo, by="Datum", all.x = TRUE)
testDatenSatz <- merge(testDatenSatz, sBrot, by="Datum", all.x = TRUE)
testDatenSatz <- merge(testDatenSatz, umsatzFachEinzelHandelSH, by="Datum", all.x = TRUE)

testDatenSatz <- testDatenSatz %>% 
  hotdeck(variable = c("Temperatur", "Windstaerke"),
          ord_var = "Datum")

#imputierte Werte von testDatenSatz graphisch überprüfen:
ggplot(testDatenSatz) +
  geom_point(aes(x = Datum, y = Temperatur, color = Temperatur_imp))

ggplot(testDatenSatz) +
  geom_point(aes(x = Datum, y = Windstaerke, color = Windstaerke_imp))


testDatenSatz <- testDatenSatz %>%
    mutate_at(c(4:26), ~replace(., is.na(.), 0))

# dummy coding der Wochentage
testDatenSatz <- dummy_cols(testDatenSatz, select_columns = "Wochentag")

testDatenSatz$year <- year(testDatenSatz$Datum)
testDatenSatz$month <- month(testDatenSatz$Datum)
testDatenSatz$day <- day(testDatenSatz$Datum)

testDatenSatz$Datum <- NULL
testDatenSatz$Wochentag <- NULL

summary(testDatenSatz)
  Windstaerke      Temperatur    WC_Bewölkung_abnehmend WC_Bewölkung_gleichbleibend
 Min.   :3.000   Min.   :14.46   Min.   :0              Min.   :0                  
 1st Qu.:5.000   1st Qu.:16.93   1st Qu.:0              1st Qu.:0                  
 Median :6.000   Median :19.59   Median :0              Median :0                  
 Mean   :5.788   Mean   :20.41   Mean   :0              Mean   :0                  
 3rd Qu.:7.000   3rd Qu.:23.40   3rd Qu.:0              3rd Qu.:0                  
 Max.   :9.000   Max.   :29.73   Max.   :0              Max.   :0                  
 WC_Bewölkung_nicht_beobachtet WC_Bewölkung_zunehmend WC_Dunst_Staub   WC_Ereignisse_letzte_h  WC_Gewitter    
 Min.   :0.0000                Min.   :0              Min.   :0.0000   Min.   :0.0000         Min.   :0.0000  
 1st Qu.:0.0000                1st Qu.:0              1st Qu.:0.0000   1st Qu.:0.0000         1st Qu.:0.0000  
 Median :0.0000                Median :0              Median :0.0000   Median :0.0000         Median :0.0000  
 Mean   :0.1538                Mean   :0              Mean   :0.1346   Mean   :0.1538         Mean   :0.1154  
 3rd Qu.:0.0000                3rd Qu.:0              3rd Qu.:0.0000   3rd Qu.:0.0000         3rd Qu.:0.0000  
 Max.   :1.0000                Max.   :0              Max.   :1.0000   Max.   :1.0000         Max.   :1.0000  
 WC_Nebel_Eisnebel    WC_Regen        WC_Schauer   WC_Schnee WC_Sprühregen     WC_Trockenereignisse
 Min.   :0         Min.   :0.0000   Min.   :0    Min.   :0   Min.   :0.00000   Min.   :0.00000     
 1st Qu.:0         1st Qu.:0.0000   1st Qu.:0    1st Qu.:0   1st Qu.:0.00000   1st Qu.:0.00000     
 Median :0         Median :0.0000   Median :0    Median :0   Median :0.00000   Median :0.00000     
 Mean   :0         Mean   :0.1731   Mean   :0    Mean   :0   Mean   :0.01923   Mean   :0.01923     
 3rd Qu.:0         3rd Qu.:0.0000   3rd Qu.:0    3rd Qu.:0   3rd Qu.:0.00000   3rd Qu.:0.00000     
 Max.   :0         Max.   :1.0000   Max.   :0    Max.   :0   Max.   :1.00000   Max.   :1.00000     
     WC_NA        Bewoelkungsgrad_gering Bewoelkungsgrad_keine Bewoelkungsgrad_mittel Bewoelkungsgrad_stark
 Min.   :0.0000   Min.   :0.00000        Min.   :0.0000        Min.   :0.0000         Min.   :0.0000       
 1st Qu.:0.0000   1st Qu.:0.00000        1st Qu.:0.0000        1st Qu.:0.0000         1st Qu.:0.0000       
 Median :0.0000   Median :0.00000        Median :0.0000        Median :0.0000         Median :0.0000       
 Mean   :0.2308   Mean   :0.05769        Mean   :0.1346        Mean   :0.4615         Mean   :0.3462       
 3rd Qu.:0.0000   3rd Qu.:0.00000        3rd Qu.:0.0000        3rd Qu.:1.0000         3rd Qu.:1.0000       
 Max.   :1.0000   Max.   :1.00000        Max.   :1.0000        Max.   :1.0000         Max.   :1.0000       
 Bewoelkungsgrad_NA  Schulferien      KielerWoche       Saisonbrot   UmsatzFEH     Temperatur_imp 
 Min.   :0          Min.   :0.0000   Min.   :0.0000   Min.   :0    Min.   :118.5   Mode :logical  
 1st Qu.:0          1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0    1st Qu.:118.5   FALSE:52       
 Median :0          Median :1.0000   Median :0.0000   Median :0    Median :122.4                  
 Mean   :0          Mean   :0.5769   Mean   :0.1731   Mean   :0    Mean   :120.8                  
 3rd Qu.:0          3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:0    3rd Qu.:122.4                  
 Max.   :0          Max.   :1.0000   Max.   :1.0000   Max.   :0    Max.   :122.4                  
 Windstaerke_imp Wochentag_Friday Wochentag_Monday Wochentag_Saturday Wochentag_Sunday Wochentag_Thursday
 Mode :logical   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000     Min.   :0.0000   Min.   :0.0000    
 FALSE:52        1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000     1st Qu.:0.0000   1st Qu.:0.0000    
                 Median :0.0000   Median :0.0000   Median :0.0000     Median :0.0000   Median :0.0000    
                 Mean   :0.1346   Mean   :0.1538   Mean   :0.1346     Mean   :0.1538   Mean   :0.1346    
                 3rd Qu.:0.0000   3rd Qu.:0.0000   3rd Qu.:0.0000     3rd Qu.:0.0000   3rd Qu.:0.0000    
                 Max.   :1.0000   Max.   :1.0000   Max.   :1.0000     Max.   :1.0000   Max.   :1.0000    
 Wochentag_Tuesday Wochentag_Wednesday      year          month            day       
 Min.   :0.0000    Min.   :0.0000      Min.   :2019   Min.   :6.000   Min.   : 1.00  
 1st Qu.:0.0000    1st Qu.:0.0000      1st Qu.:2019   1st Qu.:6.000   1st Qu.:11.00  
 Median :0.0000    Median :0.0000      Median :2019   Median :7.000   Median :17.50  
 Mean   :0.1538    Mean   :0.1346      Mean   :2019   Mean   :6.577   Mean   :17.19  
 3rd Qu.:0.0000    3rd Qu.:0.0000      3rd Qu.:2019   3rd Qu.:7.000   3rd Qu.:24.00  
 Max.   :1.0000    Max.   :1.0000      Max.   :2019   Max.   :7.000   Max.   :30.00  
save(testDatenSatz, file="Datenaufbereitung_Testdaten.Rda")

Features & Labels

features <- c(#"day",                           "month",                         "year",
              "Windstaerke",                   "Temperatur",                    "WC_Bewölkung_abnehmend",
              "WC_Bewölkung_gleichbleibend",   "WC_Bewölkung_nicht_beobachtet", "WC_Bewölkung_zunehmend",
              "WC_Dunst_Staub",                "WC_Ereignisse_letzte_h",        "WC_Gewitter",
              "WC_Nebel_Eisnebel",             "WC_Regen",                      "WC_Schauer",
              "WC_Schnee",                     "WC_Sprühregen",                 "WC_Trockenereignisse",
              "WC_NA",                         "Bewoelkungsgrad_gering",        "Bewoelkungsgrad_keine",
              "Bewoelkungsgrad_mittel",        "Bewoelkungsgrad_stark",         "Bewoelkungsgrad_NA",
              "Schulferien",                   "KielerWoche"                   
              #"Wochentag_Tuesday",
              #"Wochentag_Thursday",            #"Saisonbrot",                    "UmsatzFEH",
              #"Wochentag_Friday",              "Wochentag_Wednesday",           "Wochentag_Monday",
              #"Wochentag_Saturday",            "Wochentag_Sunday"
              )

labels <- c("Brot", "Brötchen", "Croissant", "Konditorei", "Kuchen")

Selection of Training, Validation and Test Data

# Setting the random counter to a fixed value, so the random initialization stays the same (the random split is always the same)
set.seed(1)

assignment <- sample(1:3, size = nrow(allData_dummy), prob = c(.7, .2, .1), replace = TRUE)
allData_dummy2 <- rbind(allData_dummy[assignment == 1,], synthpop_allData_dummy)

training_features <- allData_dummy2[,features]  
training_labels <- allData_dummy2[,labels]  

validation_features <- allData_dummy[assignment == 2, features]  
validation_labels <- allData_dummy[assignment == 2, labels]  

t_features <- allData_dummy[assignment == 3, features]  
t_labels <- allData_dummy[assignment == 3, labels] 

testing_features <- testDatenSatz %>% 
  select(all_of(features)) 

#are there any missing values?
table(is.na(training_features))

FALSE 
82915 
table(is.na(validation_features))

FALSE 
 9614 
table(is.na(testing_features))

FALSE 
 1196 
#summary(allData_dummy)

Modell aufstellen in Python

reticulate::repl_python()
import numpy as np
import tensorflow as tf

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import InputLayer, Dense, BatchNormalization, Dropout
from tensorflow.keras.optimizers import Adam

# The argument "input_shape" for the definition of the input layer must include 
# the number of input variables (features) used for the model. 
# To automatically calculate this number we use the function `r.training_features.keys()`, 
# which returns the list of variable names of the dataframe `training_features`.
# Then, the funtion `len()` returns the length of this list of variable names 
# (i.e. the number of variables in the input)

model = Sequential([
  InputLayer(input_shape = (len(r.training_features.keys()), )),
  BatchNormalization(),
  Dense(len(r.training_features.keys()), activation = 'swish'),
  Dropout(0.2),
  Dense(len(r.training_features.keys()), activation = 'swish'),
  Dense(5)
])

# Ausgabe einer ZUsammenfassung zur Form des MOdells, das geschätzt wird (nicht notwendig)
#model.summary()

Schätzung de neuronalen Netzes

# definition of the loss function and the optimazation function with hyperparameters
model.compile(loss="mape", optimizer=Adam(learning_rate=0.001))

#Schätzung des Modells
history = model.fit(r.training_features, r.training_labels, epochs = 300,
                    validation_data = (r.validation_features, r.validation_labels), verbose = 0)

model.save("python_model.h5")

graphische Ausgabe der Modelloptimierung

quit
# Graphische Ausgabe der Modelloptimierung

#create data
data <- data.frame(val_loss = unlist(py$history$history$val_loss),
                   loss = unlist(py$history$history$loss))

ggplot(data[-(1:10), ])+
  geom_line(aes(x = 1:length(val_loss), y = val_loss, colour = "Validation Loss")) +
  geom_line(aes(x = 1:length(loss), y = loss, colour = "Training Loss")) +
  scale_colour_manual(values = c("Training Loss"="blue", "Validation Loss" = "red")) +
  labs(title = "Loss Function Values During Optimazation") +
  xlab("Iteration Number") +
  ylab("Loss")

NA
NA

Auswertung der Schätzergebnisse

# Schätzung der (normierten) Preise für die Trainings- und Testdaten
training_predictions <- py$model$predict(training_features)

  1/113 [..............................] - ETA: 3s
113/113 [==============================] - 0s 328us/step
validation_predictions <- py$model$predict(validation_features)

 1/14 [=>............................] - ETA: 0s
14/14 [==============================] - 0s 289us/step
testing_predictions <- py$model$predict(testing_features)

1/2 [==============>...............] - ETA: 0s
2/2 [==============================] - 0s 381us/step
t_predictions <- py$model$predict(t_features)

1/7 [===>..........................] - ETA: 0s
7/7 [==============================] - 0s 313us/step
a <- format(mape(training_labels[,1], training_predictions[,1])*100, digits=3, nsmall=2)
b <- format(mape(training_labels[,2], training_predictions[,2])*100, digits=3, nsmall=2)
c <- format(mape(training_labels[,3], training_predictions[,3])*100, digits=3, nsmall=2)
d <- format(mape(training_labels[,4], training_predictions[,4])*100, digits=3, nsmall=2)
e <- format(mape(training_labels[,5], training_predictions[,5])*100, digits=3, nsmall=2)

cat(paste0("\nMAPE on the Training Data1:\t", a))

MAPE on the Training Data1: 26.70
cat(paste0("\nMAPE on the Training Data2:\t", b))

MAPE on the Training Data2: 17.94
cat(paste0("\nMAPE on the Training Data3:\t", c))

MAPE on the Training Data3: 23.69
cat(paste0("\nMAPE on the Training Data4:\t", d))

MAPE on the Training Data4: 23.44
cat(paste0("\nMAPE on the Training Data5:\t", e, "\n"))

MAPE on the Training Data5: 16.02
g <- format(mape(validation_labels[,1], validation_predictions[,1])*100, digits=3, nsmall=2)
h <- format(mape(validation_labels[,2], validation_predictions[,2])*100, digits=3, nsmall=2)
i <- format(mape(validation_labels[,3], validation_predictions[,3])*100, digits=3, nsmall=2)
j <- format(mape(validation_labels[,4], validation_predictions[,4])*100, digits=3, nsmall=2)
k <- format(mape(validation_labels[,5], validation_predictions[,5])*100, digits=3, nsmall=2)
  
cat(paste0("\nMAPE on the Validation Data1:\t", g))

MAPE on the Validation Data1:   25.48
cat(paste0("\nMAPE on the Validation Data2:\t", h))

MAPE on the Validation Data2:   18.23
cat(paste0("\nMAPE on the Validation Data3:\t", i))

MAPE on the Validation Data3:   25.02
cat(paste0("\nMAPE on the Validation Data4:\t", j))

MAPE on the Validation Data4:   22.25
cat(paste0("\nMAPE on the Validation Data5:\t", k, "\n"))

MAPE on the Validation Data5:   17.91
l <- format(mape(t_labels[,1], t_predictions[,1])*100, digits=3, nsmall=2)
m <- format(mape(t_labels[,2], t_predictions[,2])*100, digits=3, nsmall=2)
n <- format(mape(t_labels[,3], t_predictions[,3])*100, digits=3, nsmall=2)
o <- format(mape(t_labels[,4], t_predictions[,4])*100, digits=3, nsmall=2)
p <- format(mape(t_labels[,5], t_predictions[,5])*100, digits=3, nsmall=2)
  
cat(paste0("\nMAPE on the Validation Data1:\t", l))

MAPE on the Validation Data1:   26.20
cat(paste0("\nMAPE on the Validation Data2:\t", m))

MAPE on the Validation Data2:   18.14
cat(paste0("\nMAPE on the Validation Data3:\t", n))

MAPE on the Validation Data3:   25.22
cat(paste0("\nMAPE on the Validation Data4:\t", o))

MAPE on the Validation Data4:   23.45
cat(paste0("\nMAPE on the Validation Data5:\t", p, "\n"))

MAPE on the Validation Data5:   16.49
# Mean of Training and Validation Data MAPE
meanT <- c(as.double(a), as.double(b), as.double(c), as.double(d), as.double(e)) 
meanV <- c(as.double(g), as.double(h), as.double(i), as.double(j), as.double(k)) 
meanT <- c(as.double(l), as.double(m), as.double(n), as.double(o), as.double(p)) 

cat(paste0("\nMean Training MAPE: ", mean(meanT), "\n"))

Mean Training MAPE: 21.9
cat(paste0("Mean Validation MAPE: ", mean(meanV), "\n"))
Mean Validation MAPE: 21.778
cat(paste0("Mean Test MAPE: ", mean(meanT), "\n"))
Mean Test MAPE: 21.9

Grafischer vergleich der vorhergesagten & tatsächlicher Preise für die Trainings- und Validierungsdaten

data_train <- data.frame(prediction = training_predictions[,1], actual = training_labels[,1])
data_val <- data.frame(prediction = validation_predictions[,1], actual = validation_labels[,1])
data_test <- data.frame(prediction = testing_predictions[,1])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train[]) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 1") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val[,]) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 1") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 1") +
  xlab("Case Number") +
  ylab("Price in EUR") 


#------------------------- 2 -------------------------#

data_train2 <- data.frame(prediction = training_predictions[,2], actual = training_labels[,2])
data_val2 <- data.frame(prediction = validation_predictions[,2], actual = validation_labels[,2])
data_test2 <- data.frame(prediction = testing_predictions[,2])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train2) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 2") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val2) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 2") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test2) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 2") +
  xlab("Case Number") +
  ylab("Price in EUR") 


#------------------------- 3 -------------------------#

data_train3 <- data.frame(prediction = training_predictions[,3], actual = training_labels[,3])
data_val3 <- data.frame(prediction = validation_predictions[,3], actual = validation_labels[,3])
data_test3 <- data.frame(prediction = testing_predictions[,3])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train3) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 3") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val3) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 3") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test3) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 3") +
  xlab("Case Number") +
  ylab("Price in EUR") 



#------------------------- 4 -------------------------#

data_train4 <- data.frame(prediction = training_predictions[,4], actual = training_labels[,4])
data_val4 <- data.frame(prediction = validation_predictions[,4], actual = validation_labels[,4])
data_test4 <- data.frame(prediction = testing_predictions[,4])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train4) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 4") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val4) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 4") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test4) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 4") +
  xlab("Case Number") +
  ylab("Price in EUR") 


#------------------------- 5 -------------------------#

data_train5 <- data.frame(prediction = training_predictions[,5], actual = training_labels[,5])
data_val5 <- data.frame(prediction = validation_predictions[,5], actual = validation_labels[,5])
data_test5 <- data.frame(prediction = testing_predictions[,5])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train5) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 5") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val5) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 5") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test5) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 5") +
  xlab("Case Number") +
  ylab("Price in EUR") 

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCiMgUHJvamVrdCBEb2t1bWVudGF0aW9uCgpBdWZnYWJlOiBWb3JoZXJzYWdlIGRlciBVbXPDpHR6ZSB2b20gOS42LjIwMTkgYmlzIDMwLjA3LjIwMTkKCiMjIyBJbmZvcyB6dSBkZW4gZ2VnZWJlbmVuIERhdGVuCgpXYXJlbmdydXBwZW46IFwqIDEgPSBCcm90IFwqIDIgPSBCcsO2dGNoZW4gXCogMyA9IENyb2lzc2FudCBcKiA0ID0KS29uZGl0b3JlaSBcKiA1ID0gS3VjaGVuIFwqIDYgPSBTYWlzb25icm90CgojIyMgU2Fpc29uYnJvdCBtdXNzIG5pY2h0IHZvcmhlcmdlc2FndCB3ZXJkZW4hIFNpZWhlICdwcmVkaXRpb25fdGVtcGxhdGUuY3N2Jy4KCldldHRlcmRhdGVuOiBcKiBNaXR0bGVyZXIgQmV3w7Zsa3VuZ3NncmFkIGFtIFRhZyAoMCA9IG1pbiwgOCA9IG1heCkgXCoKTUl0dGxlcmUgVGVtcGVyYXR1ciBpbiBDIFwqIE1pdHRsZXJlIFdpbmRnZXNjaHdpbmRpZ2tlaXQgaW4gbS9zIFwqCldldHRlcmNvZGUgKDxodHRwOi8vd3d3LnNlZXdldHRlci1raWVsLmRlL3NlZXdldHRlci9kYXRlbl9zeW1ib2xlLmh0bT4pClwqIHVuZCBpbiBkZXIgRGF0ZWkgd2V0dGVyY29kZXMuUmRhCgojIyMgVm9yYmVyZWl0dW5nICYgYmVuw7Z0aWd0ZSBMaWJyYXJpZXMgbGFkZW4KCmBgYHtyfQpyZW1vdmUobGlzdCA9IGxzKCkpCiMgQ3JlYXRlIGxpc3Qgd2l0aCBuZWVkZWQgbGlicmFyaWVzCiMgUXVlbGxlbjoKIyAgIDEuIHN5bnRocG9wOiBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3ludGhwb3AvdmlnbmV0dGVzL3N5bnRocG9wLnBkZgojICAgMi4gCnBrZ3MgPC0gYygibHVicmlkYXRlIiwgInN0cmluZ3IiLCJ0aWR5dmVyc2UiLCAicmVhZHIiLCAKICAgICAgICAgICJmYXN0RHVtbWllcyIsICJyZXRpY3VsYXRlIiwgImdncGxvdDIiLCAiTWV0cmljcyIsICJWSU0iLCAic3ludGhwb3AiLCAiaHR0ciIpCgojIExvYWQgZWFjaCBsaXN0ZWQgbGlicmFyeSBhbmQgY2hlY2sgaWYgaXQgaXMgaW5zdGFsbGVkIGFuZCBpbnN0YWxsIGlmIG5lY2Vzc2FyeQpmb3IgKHBrZyBpbiBwa2dzKSB7CiAgaWYgKCFyZXF1aXJlKHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwa2cpCiAgICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogIH0KfQpgYGAKCiMjIyBWb3JiZXJlaXRldGUgRGF0ZW5zw6R0emUgbGFkZW4KClNrcmlwdGUsIGluIGRlbmVuIGRpZSBEYXRlbiBhdWZiZXJlaXRldCB3dXJkZW46Ci0gICBXZXR0ZXJkYXRlbiAgLS0+ICJEYXRlbmF1ZmJlcmVpdHVuZ19XZXR0ZXIuUm1kIiAKLSAgIEZlaWVydGFnZWRhdGVuICAtLT4gIkRhdGVuYXVmYmVyZWl0dW5nX0ZlaWVydGFnZS5SIiAKLSAgIFNjaHVsZmVyaWVuICAtLT4gIkRhdGVuYXVmYmVyZWl0dW5nX1NjaHVsZmVyaWVuLlIiIAotICAgVW1zYXR6ZGF0ZW4gIC0tPiAiRGF0ZW5hdWZiZXJlaXR1bmdfVW1zYXR6LlIiIAotICAgVW1zYXR6ZGF0ZW4gYXVzIGRlbSBGYWNoZWluemVsaGFuZGVsIC0tPiAiPz8iIAoKYGBge3J9CiMgTGFkZSBEYXRlbgpsb2FkKCJwal93ZXR0ZXJfZHVtbXkuUmRhIikKcGpfd2V0dGVyIDwtIHBqX3dldHRlcl9kdW1teQogIApsb2FkKCJraXdvRFQuUmRhIikKcGpfa2l3byA8LSBraXdvRFQKICAKbG9hZCgicGpfdW1zYXR6LlJkYSIpCgpsb2FkKCJzY2h1bGZlcmllbi5SZGEiKQpwal9zY2h1bGZlcmllbiA8LSBzY2h1bGZlcmllbgoKbG9hZCgidW1zYXR6RmFjaEVpbnplbEhhbmRlbFNILlJkYSIpCgojIEVyc3RlIEJldHJhY2h0dW5nIGRlciBEYXRlbgojc3VtbWFyeShwal93ZXR0ZXIpCiNzdW1tYXJ5KHBqX2tpd28pCiNzdW1tYXJ5KHBqX3Vtc2F0eikKYGBgCgojIyMgRXJzdGVsbHVuZyBkZXMgVHJhaW5pbmdzZGF0ZW5zYXR6ZW4gCgpgYGB7cn0KIyBNZXJnZSBlcnN0ZWxsdCBhdXRvbWF0aXNjaCBkaWUgU2Nobml0dG1lbmdlCiMgRGVyIFp1c2F0eiBhbGwueCA9IFRSVUUgc29yZ3QgZGFmw7xyLCBkYXNzIGtlaW5lIFplaWxlbiAoYmFzaWVyZW5kIGF1ZiBEYXRlbnNhdHogeCkgd2VnZ2Vsw7ZzY2h0IHdlcmRlbgojIFdldHRlcmRhdGVuIG5hY2ggRGF0dW0gaGluenVmw7xnZW4KcGpfdW1zYXR6X3dldHRlciA8LSBtZXJnZShwal91bXNhdHosIHBqX3dldHRlciwgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQoKIyBTY2h1bGZlcmllbiBuYWNoIERhdHVtIGhpbnp1ZsO8Z2VuCnBqX3Vtc2F0el93ZXR0ZXJfZmVyaWVuIDwtIG1lcmdlKHBqX3Vtc2F0el93ZXR0ZXIsIHBqX3NjaHVsZmVyaWVuLCBieT0iRGF0dW0iLCBhbGwueCA9IFRSVUUpCgojIEtpV28gbmFjaCBEYXR1bSBoaW56dWbDvGdlbgphbGxEYXRhIDwtIG1lcmdlKHBqX3Vtc2F0el93ZXR0ZXJfZmVyaWVuLCBwal9raXdvLCBieT0iRGF0dW0iLCBhbGwueCA9IFRSVUUpCgphbGxEYXRhIDwtIG1lcmdlKGFsbERhdGEsIHVtc2F0ekZhY2hFaW56ZWxIYW5kZWxTSCwgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQoKIyBhdWYgZmVobGVuZGUgV2VydGUgw7xiZXJwcsO8ZmVuOgphbGxEYXRhX25hIDwtIGFsbERhdGEgJT4lCiAgYWdncihjb21iaW5lZD1UUlVFLCBudW1iZXJzPVRSVUUpCgojIEltcHV0YXRpb24gVGVtcGVyYXR1ciB1bmQgV2luZHN0YWVya2UKIyBBa3R1ZWxsOiAiRGF0ZW5zcGVuZGUiIHZvbSBXZXJ0IHZvbSBWb3J0YWcKIyBaSUVMOiBNaXR0ZWx3ZXJ0IGF1cyBUZW1wZXJhdHVyIHZvbiBWb3J0YWcgdW5kIFRhZyBkYW5hY2ggLT4gQXJtYW5kbyEgOikKYWxsRGF0YSA8LSBhbGxEYXRhICU+JSAgCiAgaG90ZGVjayh2YXJpYWJsZSA9IGMoIlRlbXBlcmF0dXIiLCAiV2luZHN0YWVya2UiKSwKICAgICAgICAgIG9yZF92YXIgPSAiRGF0dW0iKQoKI2ltcHV0aWVydGUgV2VydGUgZ3JhcGhpc2NoIMO8YmVycHLDvGZlbjoKZ2dwbG90KGFsbERhdGEpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gRGF0dW0sIHkgPSBUZW1wZXJhdHVyLCBjb2xvciA9IFRlbXBlcmF0dXJfaW1wKSkKZ2dwbG90KGFsbERhdGEpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gRGF0dW0sIHkgPSBXaW5kc3RhZXJrZSwgY29sb3IgPSBXaW5kc3RhZXJrZV9pbXApKQoKIyBOQSBXZXR0ZXJjb2RlcyB6dSAwLCBkYSBTcGFsdGUgV0NfTkEgYW5naWJ0LCB3byBXZXR0ZXJjb2RlcyBnZWZlaGx0IGhhYmVuCiMgU3BhbHRlbiAxMiAtMjQKCiMgZGFzIGdsZWljaGUgZ2lsdCBiZWkgZGVyIEJld8O2bGt1bmcKIyBTcGFsdGVuIDI2IC0gMjkKCiMgd2VpdGVyZSBOQSBtaXQgMCBmw7xsbGVuLCBkb3J0IHdvIGVzIFNpbm4gZXJnaWJ0ICAKCmFsbERhdGEgPC0gYWxsRGF0YSAlPiUKICAgIG11dGF0ZV9hdChjKDEyOjM0KSwgfnJlcGxhY2UoLiwgaXMubmEoLiksIDApKQoKIyBnZW5lcmF0aW5nIHN5bnRoZXRpYyBkYXRhIApzeW50aHBvcF9hbGxEYXRhIDwtIHN5bihhbGxEYXRhKVtbInN5biJdXQoKCiMgZHVtbXkgY29kaW5nIGRlciBXb2NoZW50YWdlCmFsbERhdGFfZHVtbXkgPC0gZHVtbXlfY29scyhhbGxEYXRhLCBzZWxlY3RfY29sdW1ucyA9ICJXb2NoZW50YWciKQpzeW50aHBvcF9hbGxEYXRhX2R1bW15IDwtIGR1bW15X2NvbHMoc3ludGhwb3BfYWxsRGF0YSwgc2VsZWN0X2NvbHVtbnMgPSAiV29jaGVudGFnIikKCmFsbERhdGFfZHVtbXkkeWVhciA8LSB5ZWFyKGFsbERhdGFfZHVtbXkkRGF0dW0pCmFsbERhdGFfZHVtbXkkbW9udGggPC0gbW9udGgoYWxsRGF0YV9kdW1teSREYXR1bSkKYWxsRGF0YV9kdW1teSRkYXkgPC0gZGF5KGFsbERhdGFfZHVtbXkkRGF0dW0pCnN5bnRocG9wX2FsbERhdGFfZHVtbXkkeWVhciA8LSB5ZWFyKHN5bnRocG9wX2FsbERhdGFfZHVtbXkkRGF0dW0pCnN5bnRocG9wX2FsbERhdGFfZHVtbXkkbW9udGggPC0gbW9udGgoc3ludGhwb3BfYWxsRGF0YV9kdW1teSREYXR1bSkKc3ludGhwb3BfYWxsRGF0YV9kdW1teSRkYXkgPC0gZGF5KHN5bnRocG9wX2FsbERhdGFfZHVtbXkkRGF0dW0pCgpzYXZlKGFsbERhdGFfZHVtbXksIGZpbGU9InByb2plY3REYXRhX2R1bW15X0QuUmRhIikKc2F2ZShzeW50aHBvcF9hbGxEYXRhX2R1bW15LCBmaWxlID0gInByb2plY3RTeW50aHBvcERhdGFfZHVtbXlfRC5SZGEiKQoKYWxsRGF0YV9kdW1teSREYXR1bSA8LSBOVUxMCnN5bnRocG9wX2FsbERhdGFfZHVtbXkkRGF0dW0gPC0gTlVMTAoKI3N1bW1hcnkoYWxsRGF0YV9kdW1teSkKc2F2ZShhbGxEYXRhX2R1bW15LCBmaWxlPSJwcm9qZWN0RGF0YV9kdW1teS5SZGEiKQpzYXZlKHN5bnRocG9wX2FsbERhdGFfZHVtbXksIGZpbGUgPSAicHJvamVjdFN5bnRocG9wRGF0YV9kdW1teS5SZGEiKQoKYGBgCgojIyMgVGVzdGRhdGVuc2F0egoKYGBge3J9CiMgRXJzdGVsbGUgZWluZW4gbGVlcmVuIERhdGFmcmFtZSBtaXQgZWluZXIgU3BhbHRlIGbDvHIgZGFzIERhdHVtCnRlc3REYXRlblNhdHogPC0gZGF0YS5mcmFtZShEYXR1bSA9IGNoYXJhY3RlcigpKQoKIyBFcnN0ZWxsZSBlaW5lIFNlcXVlbnogdm9uIERhdGVuIGltIGFuZ2VnZWJlbmVuIFplaXRyYXVtCmRhdHVtX3NlcXVlbnogPC0gc2VxKGZyb20gPSBhcy5EYXRlKCIyMDE5LTA2LTA5IiksCiAgICAgICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZSgiMjAxOS0wNy0zMCIpLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJkYXlzIikKCiMgRsO8Z2UgZGllIERhdGVuIGRlciBTZXF1ZW56IGRlbSBEYXRhZnJhbWUgaGluenUKc0Jyb3QgPC0gc2VsZWN0KHBqX3Vtc2F0eiwgIkRhdHVtIiwgIlNhaXNvbmJyb3QiKQp0ZXN0RGF0ZW5TYXR6IDwtIHJiaW5kKHRlc3REYXRlblNhdHosIGRhdGEuZnJhbWUoRGF0dW0gPSBkYXR1bV9zZXF1ZW56KSkKdGVzdERhdGVuU2F0eiRXb2NoZW50YWcgPC0gd2Vla2RheXModGVzdERhdGVuU2F0eiREYXR1bSkKdGVzdERhdGVuU2F0eiA8LSBtZXJnZSh0ZXN0RGF0ZW5TYXR6LCBwal93ZXR0ZXIsIGJ5PSJEYXR1bSIsIGFsbC54ID0gVFJVRSkKdGVzdERhdGVuU2F0eiA8LSBtZXJnZSh0ZXN0RGF0ZW5TYXR6LCBwal9zY2h1bGZlcmllbiwgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQp0ZXN0RGF0ZW5TYXR6IDwtIG1lcmdlKHRlc3REYXRlblNhdHosIHBqX2tpd28sIGJ5PSJEYXR1bSIsIGFsbC54ID0gVFJVRSkKdGVzdERhdGVuU2F0eiA8LSBtZXJnZSh0ZXN0RGF0ZW5TYXR6LCBzQnJvdCwgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQp0ZXN0RGF0ZW5TYXR6IDwtIG1lcmdlKHRlc3REYXRlblNhdHosIHVtc2F0ekZhY2hFaW56ZWxIYW5kZWxTSCwgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQoKdGVzdERhdGVuU2F0eiA8LSB0ZXN0RGF0ZW5TYXR6ICU+JSAKICBob3RkZWNrKHZhcmlhYmxlID0gYygiVGVtcGVyYXR1ciIsICJXaW5kc3RhZXJrZSIpLAogICAgICAgICAgb3JkX3ZhciA9ICJEYXR1bSIpCgojaW1wdXRpZXJ0ZSBXZXJ0ZSB2b24gdGVzdERhdGVuU2F0eiBncmFwaGlzY2ggw7xiZXJwcsO8ZmVuOgpnZ3Bsb3QodGVzdERhdGVuU2F0eikgKwogIGdlb21fcG9pbnQoYWVzKHggPSBEYXR1bSwgeSA9IFRlbXBlcmF0dXIsIGNvbG9yID0gVGVtcGVyYXR1cl9pbXApKQpnZ3Bsb3QodGVzdERhdGVuU2F0eikgKwogIGdlb21fcG9pbnQoYWVzKHggPSBEYXR1bSwgeSA9IFdpbmRzdGFlcmtlLCBjb2xvciA9IFdpbmRzdGFlcmtlX2ltcCkpCgp0ZXN0RGF0ZW5TYXR6IDwtIHRlc3REYXRlblNhdHogJT4lCiAgICBtdXRhdGVfYXQoYyg0OjI2KSwgfnJlcGxhY2UoLiwgaXMubmEoLiksIDApKQoKIyBkdW1teSBjb2RpbmcgZGVyIFdvY2hlbnRhZ2UKdGVzdERhdGVuU2F0eiA8LSBkdW1teV9jb2xzKHRlc3REYXRlblNhdHosIHNlbGVjdF9jb2x1bW5zID0gIldvY2hlbnRhZyIpCgp0ZXN0RGF0ZW5TYXR6JHllYXIgPC0geWVhcih0ZXN0RGF0ZW5TYXR6JERhdHVtKQp0ZXN0RGF0ZW5TYXR6JG1vbnRoIDwtIG1vbnRoKHRlc3REYXRlblNhdHokRGF0dW0pCnRlc3REYXRlblNhdHokZGF5IDwtIGRheSh0ZXN0RGF0ZW5TYXR6JERhdHVtKQoKdGVzdERhdGVuU2F0eiREYXR1bSA8LSBOVUxMCnRlc3REYXRlblNhdHokV29jaGVudGFnIDwtIE5VTEwKCnN1bW1hcnkodGVzdERhdGVuU2F0eikKc2F2ZSh0ZXN0RGF0ZW5TYXR6LCBmaWxlPSJEYXRlbmF1ZmJlcmVpdHVuZ19UZXN0ZGF0ZW4uUmRhIikKYGBgCgojIyMgRmVhdHVyZXMgJiBMYWJlbHMKCmBgYHtyfQpmZWF0dXJlcyA8LSBjKCJkYXkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICJtb250aCIsICAgICAgICAgICAgICAgICAgICAgICAgICJ5ZWFyIiwKICAgICAgICAgICAgICAiV2luZHN0YWVya2UiLCAgICAgICAgICAgICAgICAgICAiVGVtcGVyYXR1ciIsICAgICAgICAgICAgICAgICAgICAiV0NfQmV3w7Zsa3VuZ19hYm5laG1lbmQiLAogICAgICAgICAgICAgICJXQ19CZXfDtmxrdW5nX2dsZWljaGJsZWliZW5kIiwgICAiV0NfQmV3w7Zsa3VuZ19uaWNodF9iZW9iYWNodGV0IiwgIldDX0Jld8O2bGt1bmdfenVuZWhtZW5kIiwKICAgICAgICAgICAgICAiV0NfRHVuc3RfU3RhdWIiLCAgICAgICAgICAgICAgICAiV0NfRXJlaWduaXNzZV9sZXR6dGVfaCIsICAgICAgICAiV0NfR2V3aXR0ZXIiLAogICAgICAgICAgICAgICJXQ19OZWJlbF9FaXNuZWJlbCIsICAgICAgICAgICAgICJXQ19SZWdlbiIsICAgICAgICAgICAgICAgICAgICAgICJXQ19TY2hhdWVyIiwKICAgICAgICAgICAgICAiV0NfU2NobmVlIiwgICAgICAgICAgICAgICAgICAgICAiV0NfU3Byw7xocmVnZW4iLCAgICAgICAgICAgICAgICAgIldDX1Ryb2NrZW5lcmVpZ25pc3NlIiwKICAgICAgICAgICAgICAiV0NfTkEiLCAgICAgICAgICAgICAgICAgICAgICAgICAiQmV3b2Vsa3VuZ3NncmFkX2dlcmluZyIsICAgICAgICAiQmV3b2Vsa3VuZ3NncmFkX2tlaW5lIiwKICAgICAgICAgICAgICAiQmV3b2Vsa3VuZ3NncmFkX21pdHRlbCIsICAgICAgICAiQmV3b2Vsa3VuZ3NncmFkX3N0YXJrIiwgICAgICAgICAiQmV3b2Vsa3VuZ3NncmFkX05BIiwKICAgICAgICAgICAgICAiU2NodWxmZXJpZW4iLCAgICAgICAgICAgICAgICAgICAiS2llbGVyV29jaGUiICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICJXb2NoZW50YWdfVHVlc2RheSIsCiAgICAgICAgICAgICAgIldvY2hlbnRhZ19UaHVyc2RheSIsICAgICAgICAgICAgIlNhaXNvbmJyb3QiLCAgICAgICAgICAgICAgICAgICAgIlVtc2F0ekZFSCIsCiAgICAgICAgICAgICAgIldvY2hlbnRhZ19GcmlkYXkiLCAgICAgICAgICAgICAgIldvY2hlbnRhZ19XZWRuZXNkYXkiLCAgICAgICAgICAgIldvY2hlbnRhZ19Nb25kYXkiLAogICAgICAgICAgICAgICJXb2NoZW50YWdfU2F0dXJkYXkiLCAgICAgICAgICAgICJXb2NoZW50YWdfU3VuZGF5IgogICAgICAgICAgICAgICkKCmxhYmVscyA8LSBjKCJCcm90IiwgIkJyw7Z0Y2hlbiIsICJDcm9pc3NhbnQiLCAiS29uZGl0b3JlaSIsICJLdWNoZW4iKQpgYGAKCiMjIyBTZWxlY3Rpb24gb2YgVHJhaW5pbmcsIFZhbGlkYXRpb24gYW5kIFRlc3QgRGF0YQoKYGBge3J9CiMgU2V0dGluZyB0aGUgcmFuZG9tIGNvdW50ZXIgdG8gYSBmaXhlZCB2YWx1ZSwgc28gdGhlIHJhbmRvbSBpbml0aWFsaXphdGlvbiBzdGF5cyB0aGUgc2FtZSAodGhlIHJhbmRvbSBzcGxpdCBpcyBhbHdheXMgdGhlIHNhbWUpCnNldC5zZWVkKDEpCgphc3NpZ25tZW50IDwtIHNhbXBsZSgxOjMsIHNpemUgPSBucm93KGFsbERhdGFfZHVtbXkpLCBwcm9iID0gYyguNywgLjIsIC4xKSwgcmVwbGFjZSA9IFRSVUUpCmFsbERhdGFfZHVtbXkyIDwtIHJiaW5kKGFsbERhdGFfZHVtbXlbYXNzaWdubWVudCA9PSAxLF0sIHN5bnRocG9wX2FsbERhdGFfZHVtbXkpCgp0cmFpbmluZ19mZWF0dXJlcyA8LSBhbGxEYXRhX2R1bW15MlssZmVhdHVyZXNdICAKdHJhaW5pbmdfbGFiZWxzIDwtIGFsbERhdGFfZHVtbXkyWyxsYWJlbHNdICAKCnZhbGlkYXRpb25fZmVhdHVyZXMgPC0gYWxsRGF0YV9kdW1teVthc3NpZ25tZW50ID09IDIsIGZlYXR1cmVzXSAgCnZhbGlkYXRpb25fbGFiZWxzIDwtIGFsbERhdGFfZHVtbXlbYXNzaWdubWVudCA9PSAyLCBsYWJlbHNdICAKCnRfZmVhdHVyZXMgPC0gYWxsRGF0YV9kdW1teVthc3NpZ25tZW50ID09IDMsIGZlYXR1cmVzXSAgCnRfbGFiZWxzIDwtIGFsbERhdGFfZHVtbXlbYXNzaWdubWVudCA9PSAzLCBsYWJlbHNdIAoKdGVzdGluZ19mZWF0dXJlcyA8LSB0ZXN0RGF0ZW5TYXR6ICU+JSAKICBzZWxlY3QoYWxsX29mKGZlYXR1cmVzKSkgCgojYXJlIHRoZXJlIGFueSBtaXNzaW5nIHZhbHVlcz8KdGFibGUoaXMubmEodHJhaW5pbmdfZmVhdHVyZXMpKQp0YWJsZShpcy5uYSh2YWxpZGF0aW9uX2ZlYXR1cmVzKSkKdGFibGUoaXMubmEodGVzdGluZ19mZWF0dXJlcykpCiNzdW1tYXJ5KGFsbERhdGFfZHVtbXkpCmBgYAoKIyMjIE1vZGVsbCBhdWZzdGVsbGVuIGluIFB5dGhvbgoKYGBge3B5dGhvbn0KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCB0ZW5zb3JmbG93IGFzIHRmCgpmcm9tIHRlbnNvcmZsb3cua2VyYXMubW9kZWxzIGltcG9ydCBTZXF1ZW50aWFsCmZyb20gdGVuc29yZmxvdy5rZXJhcy5sYXllcnMgaW1wb3J0IElucHV0TGF5ZXIsIERlbnNlLCBCYXRjaE5vcm1hbGl6YXRpb24sIERyb3BvdXQKZnJvbSB0ZW5zb3JmbG93LmtlcmFzLm9wdGltaXplcnMgaW1wb3J0IEFkYW0KCiMgVGhlIGFyZ3VtZW50ICJpbnB1dF9zaGFwZSIgZm9yIHRoZSBkZWZpbml0aW9uIG9mIHRoZSBpbnB1dCBsYXllciBtdXN0IGluY2x1ZGUgCiMgdGhlIG51bWJlciBvZiBpbnB1dCB2YXJpYWJsZXMgKGZlYXR1cmVzKSB1c2VkIGZvciB0aGUgbW9kZWwuIAojIFRvIGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlIHRoaXMgbnVtYmVyIHdlIHVzZSB0aGUgZnVuY3Rpb24gYHIudHJhaW5pbmdfZmVhdHVyZXMua2V5cygpYCwgCiMgd2hpY2ggcmV0dXJucyB0aGUgbGlzdCBvZiB2YXJpYWJsZSBuYW1lcyBvZiB0aGUgZGF0YWZyYW1lIGB0cmFpbmluZ19mZWF0dXJlc2AuCiMgVGhlbiwgdGhlIGZ1bnRpb24gYGxlbigpYCByZXR1cm5zIHRoZSBsZW5ndGggb2YgdGhpcyBsaXN0IG9mIHZhcmlhYmxlIG5hbWVzIAojIChpLmUuIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGluIHRoZSBpbnB1dCkKCm1vZGVsID0gU2VxdWVudGlhbChbCiAgSW5wdXRMYXllcihpbnB1dF9zaGFwZSA9IChsZW4oci50cmFpbmluZ19mZWF0dXJlcy5rZXlzKCkpLCApKSwKICBCYXRjaE5vcm1hbGl6YXRpb24oKSwKICBEZW5zZShsZW4oci50cmFpbmluZ19mZWF0dXJlcy5rZXlzKCkpLCBhY3RpdmF0aW9uID0gJ3N3aXNoJyksCiAgRHJvcG91dCgwLjIpLAogIERlbnNlKGxlbihyLnRyYWluaW5nX2ZlYXR1cmVzLmtleXMoKSksIGFjdGl2YXRpb24gPSAnc3dpc2gnKSwKICBEZW5zZSg1KQpdKQoKIyBBdXNnYWJlIGVpbmVyIFpVc2FtbWVuZmFzc3VuZyB6dXIgRm9ybSBkZXMgTU9kZWxscywgZGFzIGdlc2Now6R0enQgd2lyZCAobmljaHQgbm90d2VuZGlnKQojbW9kZWwuc3VtbWFyeSgpCmBgYAoKIyMjIFNjaMOkdHp1bmcgZGUgbmV1cm9uYWxlbiBOZXR6ZXMKCmBgYHtweXRob259CiMgZGVmaW5pdGlvbiBvZiB0aGUgbG9zcyBmdW5jdGlvbiBhbmQgdGhlIG9wdGltYXphdGlvbiBmdW5jdGlvbiB3aXRoIGh5cGVycGFyYW1ldGVycwptb2RlbC5jb21waWxlKGxvc3M9Im1hcGUiLCBvcHRpbWl6ZXI9QWRhbShsZWFybmluZ19yYXRlPTAuMDAxKSkKCiNTY2jDpHR6dW5nIGRlcyBNb2RlbGxzCmhpc3RvcnkgPSBtb2RlbC5maXQoci50cmFpbmluZ19mZWF0dXJlcywgci50cmFpbmluZ19sYWJlbHMsIGVwb2NocyA9IDMwMCwKICAgICAgICAgICAgICAgICAgICB2YWxpZGF0aW9uX2RhdGEgPSAoci52YWxpZGF0aW9uX2ZlYXR1cmVzLCByLnZhbGlkYXRpb25fbGFiZWxzKSwgdmVyYm9zZSA9IDApCgptb2RlbC5zYXZlKCJweXRob25fbW9kZWwuaDUiKQpgYGAKCiMjIyBncmFwaGlzY2hlIEF1c2dhYmUgZGVyIE1vZGVsbG9wdGltaWVydW5nCgpgYGB7cn0KIyBHcmFwaGlzY2hlIEF1c2dhYmUgZGVyIE1vZGVsbG9wdGltaWVydW5nCgojY3JlYXRlIGRhdGEKZGF0YSA8LSBkYXRhLmZyYW1lKHZhbF9sb3NzID0gdW5saXN0KHB5JGhpc3RvcnkkaGlzdG9yeSR2YWxfbG9zcyksCiAgICAgICAgICAgICAgICAgICBsb3NzID0gdW5saXN0KHB5JGhpc3RvcnkkaGlzdG9yeSRsb3NzKSkKCmdncGxvdChkYXRhWy0oMToxMCksIF0pKwogIGdlb21fbGluZShhZXMoeCA9IDE6bGVuZ3RoKHZhbF9sb3NzKSwgeSA9IHZhbF9sb3NzLCBjb2xvdXIgPSAiVmFsaWRhdGlvbiBMb3NzIikpICsKICBnZW9tX2xpbmUoYWVzKHggPSAxOmxlbmd0aChsb3NzKSwgeSA9IGxvc3MsIGNvbG91ciA9ICJUcmFpbmluZyBMb3NzIikpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIlRyYWluaW5nIExvc3MiPSJibHVlIiwgIlZhbGlkYXRpb24gTG9zcyIgPSAicmVkIikpICsKICBsYWJzKHRpdGxlID0gIkxvc3MgRnVuY3Rpb24gVmFsdWVzIER1cmluZyBPcHRpbWF6YXRpb24iKSArCiAgeGxhYigiSXRlcmF0aW9uIE51bWJlciIpICsKICB5bGFiKCJMb3NzIikKCgpgYGAKCiMjIyBBdXN3ZXJ0dW5nIGRlciBTY2jDpHR6ZXJnZWJuaXNzZQoKYGBge3J9CiMgU2Now6R0enVuZyBkZXIgKG5vcm1pZXJ0ZW4pIFByZWlzZSBmw7xyIGRpZSBUcmFpbmluZ3MtIHVuZCBUZXN0ZGF0ZW4KdHJhaW5pbmdfcHJlZGljdGlvbnMgPC0gcHkkbW9kZWwkcHJlZGljdCh0cmFpbmluZ19mZWF0dXJlcykKdmFsaWRhdGlvbl9wcmVkaWN0aW9ucyA8LSBweSRtb2RlbCRwcmVkaWN0KHZhbGlkYXRpb25fZmVhdHVyZXMpCnRlc3RpbmdfcHJlZGljdGlvbnMgPC0gcHkkbW9kZWwkcHJlZGljdCh0ZXN0aW5nX2ZlYXR1cmVzKQp0X3ByZWRpY3Rpb25zIDwtIHB5JG1vZGVsJHByZWRpY3QodF9mZWF0dXJlcykKCmEgPC0gZm9ybWF0KG1hcGUodHJhaW5pbmdfbGFiZWxzWywxXSwgdHJhaW5pbmdfcHJlZGljdGlvbnNbLDFdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKYiA8LSBmb3JtYXQobWFwZSh0cmFpbmluZ19sYWJlbHNbLDJdLCB0cmFpbmluZ19wcmVkaWN0aW9uc1ssMl0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpjIDwtIGZvcm1hdChtYXBlKHRyYWluaW5nX2xhYmVsc1ssM10sIHRyYWluaW5nX3ByZWRpY3Rpb25zWywzXSkqMTAwLCBkaWdpdHM9MywgbnNtYWxsPTIpCmQgPC0gZm9ybWF0KG1hcGUodHJhaW5pbmdfbGFiZWxzWyw0XSwgdHJhaW5pbmdfcHJlZGljdGlvbnNbLDRdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKZSA8LSBmb3JtYXQobWFwZSh0cmFpbmluZ19sYWJlbHNbLDVdLCB0cmFpbmluZ19wcmVkaWN0aW9uc1ssNV0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQoKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBUcmFpbmluZyBEYXRhMTpcdCIsIGEpKQpjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFRyYWluaW5nIERhdGEyOlx0IiwgYikpCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVHJhaW5pbmcgRGF0YTM6XHQiLCBjKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBUcmFpbmluZyBEYXRhNDpcdCIsIGQpKQpjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFRyYWluaW5nIERhdGE1Olx0IiwgZSwgIlxuIikpCgpnIDwtIGZvcm1hdChtYXBlKHZhbGlkYXRpb25fbGFiZWxzWywxXSwgdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssMV0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpoIDwtIGZvcm1hdChtYXBlKHZhbGlkYXRpb25fbGFiZWxzWywyXSwgdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssMl0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQppIDwtIGZvcm1hdChtYXBlKHZhbGlkYXRpb25fbGFiZWxzWywzXSwgdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssM10pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpqIDwtIGZvcm1hdChtYXBlKHZhbGlkYXRpb25fbGFiZWxzWyw0XSwgdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssNF0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQprIDwtIGZvcm1hdChtYXBlKHZhbGlkYXRpb25fbGFiZWxzWyw1XSwgdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssNV0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQogIApjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFZhbGlkYXRpb24gRGF0YTE6XHQiLCBnKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBWYWxpZGF0aW9uIERhdGEyOlx0IiwgaCkpCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVmFsaWRhdGlvbiBEYXRhMzpcdCIsIGkpKQpjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFZhbGlkYXRpb24gRGF0YTQ6XHQiLCBqKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBWYWxpZGF0aW9uIERhdGE1Olx0IiwgaywgIlxuIikpCgpsIDwtIGZvcm1hdChtYXBlKHRfbGFiZWxzWywxXSwgdF9wcmVkaWN0aW9uc1ssMV0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQptIDwtIGZvcm1hdChtYXBlKHRfbGFiZWxzWywyXSwgdF9wcmVkaWN0aW9uc1ssMl0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpuIDwtIGZvcm1hdChtYXBlKHRfbGFiZWxzWywzXSwgdF9wcmVkaWN0aW9uc1ssM10pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpvIDwtIGZvcm1hdChtYXBlKHRfbGFiZWxzWyw0XSwgdF9wcmVkaWN0aW9uc1ssNF0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpwIDwtIGZvcm1hdChtYXBlKHRfbGFiZWxzWyw1XSwgdF9wcmVkaWN0aW9uc1ssNV0pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQogIApjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFZhbGlkYXRpb24gRGF0YTE6XHQiLCBsKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBWYWxpZGF0aW9uIERhdGEyOlx0IiwgbSkpCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVmFsaWRhdGlvbiBEYXRhMzpcdCIsIG4pKQpjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFZhbGlkYXRpb24gRGF0YTQ6XHQiLCBvKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBWYWxpZGF0aW9uIERhdGE1Olx0IiwgcCwgIlxuIikpCgojIE1lYW4gb2YgVHJhaW5pbmcgYW5kIFZhbGlkYXRpb24gRGF0YSBNQVBFCm1lYW5UIDwtIGMoYXMuZG91YmxlKGEpLCBhcy5kb3VibGUoYiksIGFzLmRvdWJsZShjKSwgYXMuZG91YmxlKGQpLCBhcy5kb3VibGUoZSkpIAptZWFuViA8LSBjKGFzLmRvdWJsZShnKSwgYXMuZG91YmxlKGgpLCBhcy5kb3VibGUoaSksIGFzLmRvdWJsZShqKSwgYXMuZG91YmxlKGspKSAKbWVhblQgPC0gYyhhcy5kb3VibGUobCksIGFzLmRvdWJsZShtKSwgYXMuZG91YmxlKG4pLCBhcy5kb3VibGUobyksIGFzLmRvdWJsZShwKSkgCgpjYXQocGFzdGUwKCJcbk1lYW4gVHJhaW5pbmcgTUFQRTogIiwgbWVhbihtZWFuVCksICJcbiIpKQpjYXQocGFzdGUwKCJNZWFuIFZhbGlkYXRpb24gTUFQRTogIiwgbWVhbihtZWFuViksICJcbiIpKQpjYXQocGFzdGUwKCJNZWFuIFRlc3QgTUFQRTogIiwgbWVhbihtZWFuVCksICJcbiIpKQpgYGAKCiMjIyBHcmFmaXNjaGVyIHZlcmdsZWljaCBkZXIgdm9yaGVyZ2VzYWd0ZW4gJiB0YXRzw6RjaGxpY2hlciBQcmVpc2UgZsO8ciBkaWUgVHJhaW5pbmdzLSB1bmQgVmFsaWRpZXJ1bmdzZGF0ZW4KCmBgYHtyfQpkYXRhX3RyYWluIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRyYWluaW5nX3ByZWRpY3Rpb25zWywxXSwgYWN0dWFsID0gdHJhaW5pbmdfbGFiZWxzWywxXSkKZGF0YV92YWwgPC0gZGF0YS5mcmFtZShwcmVkaWN0aW9uID0gdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssMV0sIGFjdHVhbCA9IHZhbGlkYXRpb25fbGFiZWxzWywxXSkKZGF0YV90ZXN0IDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRlc3RpbmdfcHJlZGljdGlvbnNbLDFdKQoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBUcmFpbmluZ3NkYXRlbgpnZ3Bsb3QoZGF0YV90cmFpbltdKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFRyYWluaW5nIERhdGEgMSIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCiMgUGxvdCBkZXIgRXJnZWJuaXNzZSBkZXIgVmFsaWRpZXJ1bmdzZGF0ZW4KZ2dwbG90KGRhdGFfdmFsWyxdKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFZhbGlkYXRpb24gRGF0YSAxIikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRlc3RkYXRlbgpnZ3Bsb3QoZGF0YV90ZXN0KSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGxhYnModGl0bGU9IlByZWRpY3Rpb24gZm9yIHRoZSBUZXN0IERhdGEgMSIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIDIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMKCmRhdGFfdHJhaW4yIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRyYWluaW5nX3ByZWRpY3Rpb25zWywyXSwgYWN0dWFsID0gdHJhaW5pbmdfbGFiZWxzWywyXSkKZGF0YV92YWwyIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDJdLCBhY3R1YWwgPSB2YWxpZGF0aW9uX2xhYmVsc1ssMl0pCmRhdGFfdGVzdDIgPC0gZGF0YS5mcmFtZShwcmVkaWN0aW9uID0gdGVzdGluZ19wcmVkaWN0aW9uc1ssMl0pCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRyYWluaW5nc2RhdGVuCmdncGxvdChkYXRhX3RyYWluMikgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKGFjdHVhbCksIHk9YWN0dWFsLCBjb2xvdXIgPSAiQWN0dWFsIFZhbHVlcyIgKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwoIHZhbHVlcyA9IGMoIlByZWRpY3RlZCBWYWx1ZXMiPSJibHVlIiwgIkFjdHVhbCBWYWx1ZXMiPSJyZWQiKSApICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0ZWQgYW5kIEFjdHVhbCBWYWx1ZXMgZm9yIHRoZSBUcmFpbmluZyBEYXRhIDIiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFZhbGlkaWVydW5nc2RhdGVuCmdncGxvdChkYXRhX3ZhbDIpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVmFsaWRhdGlvbiBEYXRhIDIiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikKCiMgUGxvdCBkZXIgRXJnZWJuaXNzZSBkZXIgVGVzdGRhdGVuCmdncGxvdChkYXRhX3Rlc3QyKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGxhYnModGl0bGU9IlByZWRpY3Rpb24gZm9yIHRoZSBUZXN0IERhdGEgMiIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIDMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMKCmRhdGFfdHJhaW4zIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRyYWluaW5nX3ByZWRpY3Rpb25zWywzXSwgYWN0dWFsID0gdHJhaW5pbmdfbGFiZWxzWywzXSkKZGF0YV92YWwzIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDNdLCBhY3R1YWwgPSB2YWxpZGF0aW9uX2xhYmVsc1ssM10pCmRhdGFfdGVzdDMgPC0gZGF0YS5mcmFtZShwcmVkaWN0aW9uID0gdGVzdGluZ19wcmVkaWN0aW9uc1ssM10pCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRyYWluaW5nc2RhdGVuCmdncGxvdChkYXRhX3RyYWluMykgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKGFjdHVhbCksIHk9YWN0dWFsLCBjb2xvdXIgPSAiQWN0dWFsIFZhbHVlcyIgKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwoIHZhbHVlcyA9IGMoIlByZWRpY3RlZCBWYWx1ZXMiPSJibHVlIiwgIkFjdHVhbCBWYWx1ZXMiPSJyZWQiKSApICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0ZWQgYW5kIEFjdHVhbCBWYWx1ZXMgZm9yIHRoZSBUcmFpbmluZyBEYXRhIDMiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFZhbGlkaWVydW5nc2RhdGVuCmdncGxvdChkYXRhX3ZhbDMpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVmFsaWRhdGlvbiBEYXRhIDMiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikKCiMgUGxvdCBkZXIgRXJnZWJuaXNzZSBkZXIgVGVzdGRhdGVuCmdncGxvdChkYXRhX3Rlc3QzKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGxhYnModGl0bGU9IlByZWRpY3Rpb24gZm9yIHRoZSBUZXN0IERhdGEgMyIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSA0IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jCgpkYXRhX3RyYWluNCA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB0cmFpbmluZ19wcmVkaWN0aW9uc1ssNF0sIGFjdHVhbCA9IHRyYWluaW5nX2xhYmVsc1ssNF0pCmRhdGFfdmFsNCA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB2YWxpZGF0aW9uX3ByZWRpY3Rpb25zWyw0XSwgYWN0dWFsID0gdmFsaWRhdGlvbl9sYWJlbHNbLDRdKQpkYXRhX3Rlc3Q0IDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRlc3RpbmdfcHJlZGljdGlvbnNbLDRdKQoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBUcmFpbmluZ3NkYXRlbgpnZ3Bsb3QoZGF0YV90cmFpbjQpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVHJhaW5pbmcgRGF0YSA0IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpIAoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBWYWxpZGllcnVuZ3NkYXRlbgpnZ3Bsb3QoZGF0YV92YWw0KSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFZhbGlkYXRpb24gRGF0YSA0IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRlc3RkYXRlbgpnZ3Bsb3QoZGF0YV90ZXN0NCkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0aW9uIGZvciB0aGUgVGVzdCBEYXRhIDQiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSA1IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jCgpkYXRhX3RyYWluNSA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB0cmFpbmluZ19wcmVkaWN0aW9uc1ssNV0sIGFjdHVhbCA9IHRyYWluaW5nX2xhYmVsc1ssNV0pCmRhdGFfdmFsNSA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB2YWxpZGF0aW9uX3ByZWRpY3Rpb25zWyw1XSwgYWN0dWFsID0gdmFsaWRhdGlvbl9sYWJlbHNbLDVdKQpkYXRhX3Rlc3Q1IDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRlc3RpbmdfcHJlZGljdGlvbnNbLDVdKQoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBUcmFpbmluZ3NkYXRlbgpnZ3Bsb3QoZGF0YV90cmFpbjUpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVHJhaW5pbmcgRGF0YSA1IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpIAoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBWYWxpZGllcnVuZ3NkYXRlbgpnZ3Bsb3QoZGF0YV92YWw1KSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFZhbGlkYXRpb24gRGF0YSA1IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRlc3RkYXRlbgpnZ3Bsb3QoZGF0YV90ZXN0NSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0aW9uIGZvciB0aGUgVGVzdCBEYXRhIDUiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCmBgYA==